import { isUnaryTag } from "../utils/index.js"

export default function parse(template) {
    let html = template, stack = [], root = null
    while (html.trim()) {
        // 注释节点 
        if (html.indexOf("<!--") === 0) {
            const startIdx = html.indexOf("-->")
            html = html.slice(startIdx + 3)
            continue
        }
        let startIdx = html.indexOf("<")
        if (startIdx === 0) {
            // 判断是开标签还是闭标签
            if (html.indexOf("</") === 0) {
                // 闭标签
                processEndTag()
            } else {
                processStartTag()
            }
        } else if (startIdx > 0) {
            // 前面是文本 test<div>test</div>  取出文本 入栈
            let text = html.slice(0, startIdx)
            if (stack.length) {
                // 栈顶元素存在 处理栈顶标签内的文本
                processChars(text)
            }
            html = html.slice(startIdx)
        } else {
            // 无用的东西 直接丢掉
        }
    }
    return root
    /**
     * 处理开始标签
     * @param {*} tag 
     * @param {*} attrsMap 
     */
    function processStartTag() {
        let tagName = "", attrs = ""
        const endIdx = html.indexOf(">");
        const content = html.slice(1, endIdx).trim()
        html = html.slice(endIdx + 1)
        const firstSpaceIdx = content.indexOf(" ")
        //  没有属性的情况 content就是标签名了
        if (firstSpaceIdx === -1) {
            tagName = content
        } else {
            // div id="app" 切割标签名
            tagName = content.slice(0, firstSpaceIdx)
            attrs = content.slice(firstSpaceIdx + 1)
        }
        // 切割属性
        attrs = attrs ? attrs.split(" ") : []
        /* 生成属性抽象语法树 */
        let attrsMap = parseAttrs(attrs)
        /* 转成DOM AST */
        let elementAST = generatAST(tagName, attrsMap)
        // 如果为空 那么为第一个标签
        if (!root) {
            root = elementAST
        }
        // 将开标签入栈 在遇到闭标签的时候出栈
        stack.push(elementAST)
        // 如果是自闭合标签 那么处理完开始标签就可以直接开始处理属性元素了
        if (isUnaryTag(tagName)) {
            processElement()
        }
    }
    /**
     * 处理闭合标签
     */
    function processEndTag() {
        // 截取闭合标签的后面部分
        html = html.slice(html.indexOf(">") + 1)
        // 接着再去匹配栈顶元素
        processElement()
    }
    /**
      * 处理属性AST
      * @param {Array<string>} attrs 属性字符串数组 attrs: [id="app",name="xxx"]
      */
    function parseAttrs(attrs) {
        let attrsMap = {}
        for (let attr of attrs) {
            const [prop, value] = attr.split("=")
            // 拿到正确格式的键值对
            attrsMap[prop] = value.replace(/\"/g, "")
        }
        return attrsMap
    }
    /**
     * 处理ast格式
     * @param {string} tag 
     * @param {*} attrMap 
     * @returns 
     */
    function generatAST(tag, attrMap) {
        return {
            type: 1,
            tag,
            rawAttr: attrMap,
            children: []
        }
    }
    /* 处理自闭合标签 */
    function processElement() {
        const peekAST = stack.pop()
        const { rawAttr } = peekAST
        let attrs = Object.keys(rawAttr) // ["v-model", "v-bind"]
        peekAST.attr = {}
        if (attrs.includes("v-model")) {
            // 处理v-model指令
            processVModel(peekAST)
        } else if (attrs.find((item) => item.match(/v-bind:(.*)|:(.*)/))) {
            // 处理v-bind指令
            processVBind(peekAST, RegExp.$1, rawAttr[`v-bind:${RegExp.$1}`] || rawAttr[`:${RegExp.$1}`])
            // delete rawAttr[`v-bind:${RegExp.$1}`]
            // delete rawAttr[`:${RegExp.$1}`]
        } else if (attrs.find((item) => item.match(/@(.*)|v-on:(.*)/))) {
            // 处理VOn指令
            processVOn(peekAST, RegExp.$1, rawAttr[`v-on:${RegExp.$1}`] || rawAttr[`@${RegExp.$1}`])
            // delete rawAttr[`v-on:${RegExp.$1}`]
            // delete rawAttr[`@${RegExp.$1}`]
        }
        let len = stack.length
        if (len) {
            // 拿出对应的父级AST添加进children
            stack[len - 1].children.push(peekAST)
            peekAST.parent = stack[len - 1]
        }
    }
    /**
     * 处理vmodel指令
     * @param {*} ast 
     */
    function processVModel(ast) {
        const { rawAttr, tag, attr } = ast
        const { type, 'v-model': vModelValue } = rawAttr
        if (tag === "input" || type === "checkbox") {
            attr.vModel = { tag, type, value: vModelValue }
        } else if (tag === "textarea") {
            attr.vModel = { tag, value: vModelValue }
        }
        else if (tag === "select") {
            attr.vModel = { tag, value: vModelValue }
        }
    }
    /**
     * 处理vbind指令
     * @param {*} ast 
     * @param {*} bindKey 
     * @param {*} bindValue 
     */
    function processVBind(ast, bindKey, bindValue) {
        ast.attr.vBind = { [bindKey]: bindValue }
    }
    /**
     * 处理von指令
     * @param {*} ast 
     * @param {*} onKey 
     * @param {*} onValue 
     */
    function processVOn(ast, onKey, onValue) {
        ast.attr.vOn = { [onKey]: onValue }
    }
    /**
     * 处理标签所包含的文本
     * @param {string} text 
     */
    function processChars(text) {
        if (!text.trim()) return
        const textAST = {
            type: 3,
            text
        }
        // 处理文本是表达式的节点
        if (text.match(/{{(.*)}}/)) {
            textAST.expression = RegExp.$1.trim()
        }
        // 将文本添加到父节点中
        stack[stack.length - 1].children.push(textAST)
    }
}
